package fms.controller;

import fms.dao.CptInfoService;
import fms.dao.CptSendService;
import fms.dao.PlanCptWdService;
import fms.model.CptInfo;
import fms.model.CptSend;
import fms.model.PlanCptWd;
import fms.util.*;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <P>FileName: ParseFlightPlanService.java
 *
 *         <P>Description: 解析飞行计划服务
 *         <P>Version: v1.0
 *         <P>History:
 */
public class JetplanParseController implements IFlyPlanParseController {
    private Logger logger = Logger.getLogger(JetplanParseController.class);

    // 存入CPT_INFO\PLAN_CPT_WD表的记录列表数据
    private List cptInfoList = new ArrayList();
    private List planCptWdList = new ArrayList();

    private JetplanParseRuleVO paramVO = new JetplanParseRuleVO();
    // 航空公司二字码和三字码对照表
    private Map<String, String> mapAirlinesCodes = new HashMap<String, String>();
    // 存储"风向、风速、风温"存在位数不合法的航路点名称
    private Map<String, String> mapInvalidCptNames = new HashMap<String, String>();

    // 提供数据库交互服务的对象
    private CptInfoService cptInfoService = null;
    private PlanCptWdService planCptWdService = null;

    private CptSend cptSend;
    private CptSendService cptSendService= null;

    // 读取配置文件，加载解析Jetplan所需的参数信息
    public JetplanParseController() {

        cptInfoService = ConfUtil.getSingletonApplicationContextDao().getBean("cptInfoService", CptInfoService.class);
        planCptWdService = ConfUtil.getSingletonApplicationContextDao().getBean("planCptWdService", PlanCptWdService.class);
        cptSendService= ConfUtil.getSingletonApplicationContextDao().getBean("cptSendService", CptSendService.class);
        java.io.File fParseRule = null;
        fParseRule = new java.io.File(ConfUtil.props.getProperty("parseRuleFilePath"));

        final SAXReader reader = new SAXReader();
        try {
            logger.info("Jetplan解析参数加载中！");
            final Document paraseRulenDoc = reader.read(fParseRule);

            // 将航空公司的二字码和三字码对照表存入MAP
            List airlineCodeList = paraseRulenDoc.selectNodes("/FPLParseRules/JetplanParseRule/AirlinesCodes/AirlineCode");
            Iterator it = airlineCodeList.iterator();
            while (it.hasNext()) {
                Element airlinesCode = (Element) it.next();
                mapAirlinesCodes.put(airlinesCode.attributeValue("id"), airlinesCode.getTextTrim());
            }

            Element uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/planId");
            paramVO.setPlanIdAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setPlanIdPath(uniqueObject.getTextTrim());
            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/planDate");
            paramVO.setPlanDateAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setPlanDatePath(uniqueObject.getTextTrim());

            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/flightId");
            paramVO.setFlightIdAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setFlightIdPath(uniqueObject.getTextTrim());
            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/aircraftId");
            paramVO.setAircraftIdAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setAircraftIdPath(uniqueObject.getTextTrim());

            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/dep");
            paramVO.setDepAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setDepPath(uniqueObject.getTextTrim());
            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/arr");
            paramVO.setArrAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setArrPath(uniqueObject.getTextTrim());

            uniqueObject = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/uniqueObject/route");
//            paramVO.setRoutelineAttribute(uniqueObject.attributeValue("attributeName"));
            paramVO.setRoutelinePath(uniqueObject.getTextTrim());


            Element cptPosInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptPosInfo");
            paramVO.setCptPosAttribute(cptPosInfo.attributeValue("attributeName"));
            paramVO.setCptPosPath(cptPosInfo.attributeValue("prefixPath"));
            cptPosInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptPosInfo/lon");
            paramVO.setLonAttribute(cptPosInfo.attributeValue("attributeName"));
            paramVO.setLonPath(cptPosInfo.getTextTrim());
            cptPosInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptPosInfo/lat");
            paramVO.setLatAttribute(cptPosInfo.attributeValue("attributeName"));
            paramVO.setLatPath(cptPosInfo.getTextTrim());

            Element cptWindTempInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptWindTempInfo");
            paramVO.setCptWindTempAttribute(cptWindTempInfo.attributeValue("attributeName"));
            paramVO.setCptWindTempPath(cptWindTempInfo.attributeValue("prefixPath"));
            cptWindTempInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptWindTempInfo/windDir");
            paramVO.setWindDirAttribute(cptWindTempInfo.attributeValue("attributeName"));
            paramVO.setWindDirPath(cptWindTempInfo.getTextTrim());
            cptWindTempInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptWindTempInfo/windVel");
            paramVO.setWindVelAttribute(cptWindTempInfo.attributeValue("attributeName"));
            paramVO.setWindVelPath(cptWindTempInfo.getTextTrim());
            cptWindTempInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptWindTempInfo/windTep");
            paramVO.setWindTepAttribute(cptWindTempInfo.attributeValue("attributeName"));
            paramVO.setWindTepPath(cptWindTempInfo.getTextTrim());
            cptWindTempInfo = (Element) paraseRulenDoc.selectSingleNode("/FPLParseRules/JetplanParseRule/cptWindTempInfo/alt");
            paramVO.setAltAttribute(cptWindTempInfo.attributeValue("attributeName"));
            paramVO.setAltPath(cptWindTempInfo.getTextTrim());

            logger.info("Jetplan解析参数加载结束，开始解析数据！");
        } catch (final DocumentException e) {

            logger.error(e.getMessage());
        }
    }


    @Override
    public boolean parseDataSource(File fDataSource) {
        return false;
    }

    @Override
    public boolean parseDataSource(String strDataSource) {
        // TODO Auto-generated method stub
        SAXReader readerParseRule = new SAXReader();
        ByteArrayInputStream stringInputStream = new ByteArrayInputStream(strDataSource.getBytes());

        try {
            Document jetplanDoc = readerParseRule.read(stringInputStream);
            List cptList = null;
            Iterator it = null;
            /****************1、先提取出两张表都需要的数据节点*************/
            // 航路信息独自在一个 /FlightPlan/Route 节点内
            Element flyRoute = (Element) jetplanDoc.selectSingleNode(paramVO.getRoutelinePath());

            // 航班号、机尾号、起飞机场、落地机场在相同的节点内 /FlightPlan/FilingStrip
            Element flyInfo = (Element) jetplanDoc.selectSingleNode(paramVO.getFlightIdPath());

            // 飞行计划ID、计划生成时间在相同的节点内 /FlightPlan/Info
            Element flyPlan = (Element) jetplanDoc.selectSingleNode(paramVO.getPlanIdPath());
            String planCreateTime = flyPlan.attributeValue(paramVO.getPlanDateAttribute());
            if (ObjectUtil.isBlank(planCreateTime)) {
                logger.error("飞行计划时间为空！");
                return false;
            }
            planCreateTime = planCreateTime.replace('T', ' ');
            planCreateTime = planCreateTime.replace('Z', ' ');

            /*********************2、生成CPT_INFO数据******************/
            // 用于飞行计划的位数合法性校验
            Matcher matcher = null;
            Pattern pattern = null;

            // 航路点、经度、纬度
            cptList = jetplanDoc.selectNodes(paramVO.getCptPosPath());
            it = cptList.iterator();
            while (it.hasNext()) {
                CptInfo cptInfoRecord = new CptInfo();
                Element cptPosNode = (Element) it.next();

                cptInfoRecord.setCptName(cptPosNode.attributeValue(paramVO.getCptPosAttribute()).trim());

                // 下面的经纬度解析入库方式，是为了跟春秋格式保持一致（南北纬最大是90度，东西经最大是180度）
                Element latPos = (Element) cptPosNode.selectSingleNode(paramVO.getLatPath());
                // <Lat direction="N" degrees="35" minutes="22.9" />    ==>   latValue=N35229
                String latValue = latPos.attributeValue(paramVO.getLatAttribute().split("\\.")[0])
                        + latPos.attributeValue(paramVO.getLatAttribute().split("\\.")[1])
                        + latPos.attributeValue(paramVO.getLatAttribute().split("\\.")[2]).replace(".", "");
                cptInfoRecord.setLat(latValue);

                Element lonPos = (Element) cptPosNode.selectSingleNode(paramVO.getLonPath());
                String lonValue = lonPos.attributeValue(paramVO.getLonAttribute().split("\\.")[0])
                        + lonPos.attributeValue(paramVO.getLonAttribute().split("\\.")[1])
                        + lonPos.attributeValue(paramVO.getLonAttribute().split("\\.")[2]).replace(".", "");
                cptInfoRecord.setLon(lonValue);

                if (ObjectUtil.isBlank(cptInfoRecord.getCptName())
                        || !ValidCheckAviation.RegexCheck(latValue, ValidCheckAviation.CheckType.WindLat.ordinal())
                        || !ValidCheckAviation.RegexCheck(lonValue, ValidCheckAviation.CheckType.WindLon.ordinal())
                        ) {
                    logger.error("航路点、经度、纬度不合法，航路点{" + cptInfoRecord.getCptName() + "}信息已丢弃：");
                    mapInvalidCptNames.put(cptInfoRecord.getCptName(), "1");
                    continue;
                }
                cptInfoRecord.setPlanId(flyPlan.attributeValue(paramVO.getPlanIdAttribute()));
                cptInfoRecord.setPlanDate(DateUtil.StrToDate(planCreateTime, SysConstants.FORMAT_DATETIME_FULL));

                String aircraftId = flyInfo.attributeValue(paramVO.getAircraftIdAttribute());
                if (ObjectUtil.isBlank(aircraftId)) {
                    logger.error("机尾号为空！");
                    return false;
                }
                cptInfoRecord.setAircraftId("B-" + aircraftId.substring(1, aircraftId.length()));

                // *****start: 航班号解析相关
                // Jetplan中的航班号解析两种情况：
                // 1、以“三字码 + 序号”组成；
                // 2、以“二字码 + 序号”组成
                // 从Map（解析规则配置文件）中获取航空公司三字码对应的二字码
                String airline2Code =
                        mapAirlinesCodes.get(flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 3));
//                if (ObjectUtil.isBlank(airline2Code)) {
//                    logger.error("解析规则配置文件中不存在三字码‘"
//                            + flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 3)
//                            + "’所对应的二字码");
//                    return false;
//                }
                if (ObjectUtil.isBlank(airline2Code)) {
                    // 不是三字码开头，使用二字码（海航飞行计划中出现过航班号HUX）
                    cptInfoRecord.setAirlines( flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 2) );
                } else {
                    cptInfoRecord.setAirlines(airline2Code);
                }

                String flightId = flyInfo.attributeValue(paramVO.getFlightIdAttribute());
                if (ObjectUtil.isBlank(flightId)) {
                    logger.error("航班号为空！");
                    return false;
                }
                if (ObjectUtil.isBlank(airline2Code)) {
                    // 不是三字码开头，按二字码方式解析
//System.out.println(  flightId.length() );
//System.out.println( flightId.substring(2, flightId.length()) );
                    // 二字码后的序号，第一位是0（国际航班），仅入库非0部分
                    if ( '0' == flightId.charAt(2) ) {
                        cptInfoRecord.setFlightId(flightId.substring(3, flightId.length()));
                    } else {
                        cptInfoRecord.setFlightId(flightId.substring(2, flightId.length()));
                    }
                } else {
                    // 类吉祥的，三字码开头航班号
                    if ( '0' == flightId.charAt(3) ) {
                        cptInfoRecord.setFlightId(flightId.substring(4, flightId.length()));
                    } else {
                        cptInfoRecord.setFlightId(flightId.substring(3, flightId.length()));
                    }
                }
                // *****end: 航班号解析相关


                cptInfoRecord.setDep(flyInfo.attributeValue(paramVO.getDepAttribute()));
                if (ObjectUtil.isBlank(cptInfoRecord.getDep())) {
                    logger.error("起飞机场为空！");
                    return false;
                }

                cptInfoRecord.setArr(flyInfo.attributeValue(paramVO.getArrAttribute()));
                if (ObjectUtil.isBlank(cptInfoRecord.getArr())) {
                    logger.error("落地机场为空！");
                    return false;
                }

                // 海航的飞行计划需要取出该值（吉祥不用）
                cptInfoRecord.setRouteline(flyRoute.getText());
//                System.out.println(flyRoute.getText());
                if (ObjectUtil.isBlank(cptInfoRecord.getRouteline())) {
                    logger.error("缺少航路信息 /FlightPlan/Route/RouteLineVersion2！");
                    return false;
                }

                cptInfoList.add(cptInfoRecord);



                cptSend = new CptSend();
                // 海航的飞行计划需要取出该值（吉祥不用） 航路信息
                cptSend.setRouteline(flyRoute.getText());
                cptSend.setArr(flyInfo.attributeValue(paramVO.getArrAttribute()));
                cptSend.setDep(flyInfo.attributeValue(paramVO.getDepAttribute()));

                if (ObjectUtil.isBlank(airline2Code)) {
                    // 不是三字码开头，使用二字码（海航飞行计划中出现过航班号HUX）
                    cptSend.setAirlines( flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 2) );
                } else {
                    cptSend.setAirlines(airline2Code);
                }

                if (ObjectUtil.isBlank(airline2Code)) {
                    // 不是三字码开头，按二字码方式解析
                    // 二字码后的序号，第一位是0（国际航班），仅入库非0部分
                    if ( '0' == flightId.charAt(2) ) {
                        cptSend.setFlightId(flightId.substring(0, 2) + flightId.substring(3, flightId.length()));
                    } else {
                        cptSend.setFlightId(flightId.substring(0, 2) + flightId.substring(2, flightId.length()));
                    }
                } else {
                    // 类吉祥的，三字码开头航班号
                    if ( '0' == flightId.charAt(3) ) {
                        cptSend.setFlightId(airline2Code + flightId.substring(4, flightId.length()));
                    } else {
                        cptSend.setFlightId(airline2Code + flightId.substring(3, flightId.length()));
                    }
                }
                 // *****end: 航班号解析相关


                cptSend.setAircraftId("B-" + aircraftId.substring(1, aircraftId.length()));
                cptSend.setPlanId(flyPlan.attributeValue(paramVO.getPlanIdAttribute()));
                cptSend.setPlanDate(DateUtil.StrToDate(planCreateTime, SysConstants.FORMAT_DATETIME_FULL));
                cptSend.setSendType("2");
                cptSend.setSendStatus("2");
                cptSend.setCaptainStatus("0");


            }
            /******************3、生成PLAN_CPT_WD数据*******************/
            // 风向、风速、风温、高度层
            cptList = jetplanDoc.selectNodes(paramVO.getCptWindTempPath());
            it = cptList.iterator();
            while (it.hasNext()) {
                Element cptWdNode = (Element) it.next();
//				System.out.println(cptWdNode.getName() + "-@@@->" + cptWdNode.attributeValue(paramVO.getCptWindTempAttribute()));
                List<Element> subList = cptWdNode.elements();
                for (Element flightLevelNode : subList) {
                    PlanCptWd cptWdRecord = new PlanCptWd();

                    cptWdRecord.setCptName(cptWdNode.attributeValue(paramVO.getCptWindTempAttribute()).trim());
                    // 丢弃本航路点的各个高度层风温数据
                    if (ObjectUtil.isBlank(cptWdRecord.getCptName())
                            || null != mapInvalidCptNames.get(cptWdRecord.getCptName())) {
                        break;
                    }
                    cptWdRecord.setWindVel(Float.valueOf(flightLevelNode.attributeValue(paramVO.getWindVelAttribute())));
                    cptWdRecord.setWindDir(Float.valueOf(flightLevelNode.attributeValue(paramVO.getWindDirAttribute())));
                    if (!ValidCheckAviation.RegexCheck(flightLevelNode.attributeValue(paramVO.getWindVelAttribute()), ValidCheckAviation.CheckType.WindVel.ordinal())
                            || !ValidCheckAviation.RegexCheck(flightLevelNode.attributeValue(paramVO.getWindDirAttribute()), ValidCheckAviation.CheckType.WindDir.ordinal())
                            ) {
                        logger.error("风速、风向不合法！");
                        continue;
                    }
                    //遍历得到每个元素的名字和内容
//					System.out.println( " " + e.getName() + "-->" +  + "   " +  + "   " +  + "   "  + e.attributeValue());
                    cptWdRecord.setPlanId(flyPlan.attributeValue(paramVO.getPlanIdAttribute()));

                    String aircraftId = flyInfo.attributeValue(paramVO.getAircraftIdAttribute());
                    if (ObjectUtil.isBlank(aircraftId)) {
                        logger.error("机尾号为空！");
                        return false;
                    }
                    cptWdRecord.setAircraftId("B-" + aircraftId.substring(1, aircraftId.length()));


                    // *****start: 航班号解析相关
                    // Jetplan中的航班号解析两种情况：
                    // 1、以“三字码 + 序号”组成；
                    // 2、以“二字码 + 序号”组成
                    // 从Map（解析规则配置文件）中获取航空公司三字码对应的二字码
                    String airline2Code =
                            mapAirlinesCodes.get(flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 3));
//                if (ObjectUtil.isBlank(airline2Code)) {
//                    logger.error("解析规则配置文件中不存在三字码‘"
//                            + flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 3)
//                            + "’所对应的二字码");
//                    return false;
//                }
                    if (ObjectUtil.isBlank(airline2Code)) {
                        // 不是三字码开头，使用二字码（海航飞行计划中出现过航班号HUX）
                        cptWdRecord.setAirlines( flyInfo.attributeValue(paramVO.getFlightIdAttribute()).substring(0, 2) );
                    } else {
                        cptWdRecord.setAirlines(airline2Code);
                    }

                    String flightId = flyInfo.attributeValue(paramVO.getFlightIdAttribute());
                    if (ObjectUtil.isBlank(flightId)) {
                        logger.error("航班号为空！");
                        return false;
                    }
                    if (ObjectUtil.isBlank(airline2Code)) {
                        // 不是三字码开头，按二字码方式解析
                        if ( '0' == flightId.charAt(2) ) {
                            cptWdRecord.setFlightId(flightId.substring(3, flightId.length()));
                        } else {
                            cptWdRecord.setFlightId(flightId.substring(2, flightId.length()));
                        }
                    } else {
                        // 类吉祥的，三字码开头航班号
                        if ( '0' == flightId.charAt(3) ) {
                            cptWdRecord.setFlightId(flightId.substring(4, flightId.length()));
                        } else {
                            cptWdRecord.setFlightId(flightId.substring(3, flightId.length()));
                        }
                    }

                    // *****end: 航班号解析相关


                    if (ObjectUtil.isBlank(flightLevelNode.attributeValue(paramVO.getAltAttribute()))) {
                        logger.error("高度层为空！");
                        return false;
                    }
                    cptWdRecord.setAlt(Float.valueOf(flightLevelNode.attributeValue(paramVO.getAltAttribute())));

                    cptWdRecord.setPlanDate(DateUtil.StrToDate(planCreateTime, SysConstants.FORMAT_DATETIME_FULL));

                    String cptFlyLevTemp = flightLevelNode.attributeValue(paramVO.getWindTepAttribute());
                    if (!ValidCheckAviation.RegexCheck(cptFlyLevTemp, ValidCheckAviation.CheckType.WindTemp.ordinal())) {
                        logger.error("风温信息不合法！");
                        continue;
                    } else {
                        if ('P' == cptFlyLevTemp.charAt(0)) {
                            cptWdRecord.setWindTep(Float.valueOf(cptFlyLevTemp.substring(1)));
                        } else if ('M' == cptFlyLevTemp.charAt(0)) {
                            cptWdRecord.setWindTep(Float.valueOf("-" + cptFlyLevTemp.substring(1)));
                        } else if (0 == Float.valueOf(cptFlyLevTemp)) {
                            cptWdRecord.setWindTep(Float.valueOf(cptFlyLevTemp));
                        } else {
                            // 如果没有符号就当成整数（且温度不为0），可能导致偏差较大，所以，丢弃本条记录
                            continue;
                        }
                    }

                    planCptWdList.add(cptWdRecord);
                }
            }

        } catch (Exception e) {

            logger.error(e.getMessage());
            return false;
        }
        return true;
    }

    @Override
    public void saveToDB() {
        try {
            // CPT_INFO 数据入库
            cptInfoService.save(cptInfoList);
            // PLAN_CPT_WD 数据入库
            planCptWdService.save(planCptWdList);
            //CPT_SEND数据入库
            cptSendService.saveOrUpdate(cptSend);

        } catch (Exception e) {
            logger.error("数据库访问异常：" + e.getMessage());
        }
    }


}